#include "Computation.h"

using namespace std; 

void * computationInt(void * ptr){
  //Initiate the environment containing all the specifications of the computation
  Environment * env = (Environment*) ptr;
  long nbLoop = env->getOperations();

  struct timeval tbegin, tend;
  gettimeofday(&tbegin, NULL);

  //Integer Computation
  for (long i =0; i< nbLoop; i++); 

  gettimeofday(&tend, NULL);
  //Compute time spent
  float totalTimeSec = tend.tv_sec - tbegin.tv_sec;
  float totalTimeMS  = tend.tv_usec - tbegin.tv_usec; 

  float result = 0; 

  //Compute the IOPS
  result = nbLoop/((totalTimeMS + (totalTimeSec*1000000))/1000000);

  //Store result in the environment for further processing
  env->addResult(result); 
  
  return NULL;
}

void * computationFloat(void* ptr){
  //Initiate the environment containing all the specifications of the computation
  Environment * env = (Environment*) ptr;
  float nbLoop = (float) env->getOperations();

  struct timeval tbegin, tend;   // time measure structure 
  float test = (float) 1.0; 
  gettimeofday(&tbegin, NULL);

  //Floating Computation
  while (test < nbLoop ) {
    test += 1.0;
  }

  gettimeofday(&tend, NULL);
  //Compute time spent
  float totalTimeSec = tend.tv_sec - tbegin.tv_sec;
  float totalTimeMS  = tend.tv_usec - tbegin.tv_usec; 

  float result = 0; 
  //Compute FLOPS
  result = nbLoop/((totalTimeMS + (totalTimeSec*1000000))/1000000);

  //Store result in the environment for further processing
  env->addResult(result);
  
  return NULL;
}

Computation::Computation(){
  _env = new Environment();
}

Computation::Computation(Environment * env){
  _env = env ; 
}

Computation::~Computation(){
  _env->~Environment(); 
}

void Computation::runTests(){
  //Set the precision of cerr to 15 digits
  cerr << setprecision(15);

  int nbThread = _env->getNbThread();

  switch(_env->getComputation()){

  case 1 : // INT 
    // Launch threads
    for (int i =0; i < nbThread; i++){
      pthread_create(&(_env->getThread()[i]), NULL, &computationInt, (void *)(_env));
    }
    for(int i=0; i< nbThread; i++){
      // Wait for all threads to finish
      pthread_join(_env->getThread()[i], NULL);
    }
    cout << "Result for Integer Computation : " << _env->getResult() << "IOPS" << endl ;  
    cerr << _env->getResult() << ";" << endl ;     
    break;
 
  case 2: // FLOAT
    // Launch threads
    for (int i =0; i < nbThread; i++){
      pthread_create(&(_env->getThread()[i]), NULL, &computationFloat, (void *)(_env));
    }
    for(int i=0; i< nbThread; i++){
      // Wait for all threads to finish
      pthread_join(_env->getThread()[i], NULL);
    }
    cout << "Result for Floating computation : " << _env->getResult() << "FLOPS" << endl ; 
    cerr << ";" << _env->getResult() << ";" << endl ; 
    break; 

  case 0: // BOTH
    // Launch threads for INT computations
    for (int i =0; i < nbThread; i++){
      pthread_create(&(_env->getThread()[i]), NULL, &computationInt, (void *)(_env));
    }
    for(int i=0; i< nbThread; i++){
      // Wait for all the INT threads to finish
      pthread_join(_env->getThread()[i], NULL);
    }
    cout << "Result for Integer Computation : " << _env->getResult() << "IOPS" << endl ;
    cerr << _env->getResult() << ";" << endl ;  
    _env->resetResult();

    //Launch threads for FLOAT computations
    for (int i =0; i < nbThread; i++){
      pthread_create(&(_env->getThread()[i]), NULL, &computationFloat, (void *)(_env));
    }
    for(int i=0; i< nbThread; i++){
      // Wait for all the FLOAT threads to finish
      pthread_join(_env->getThread()[i], NULL);
    }
    cout << "Result for Floating computation: " << _env->getResult() << "IOPS" << endl ;
    cerr << ";" <<_env->getResult() << ";" << endl ; 
    break;
  default: //BOTH

   //Launch threads for INT computations
    for (int i =0; i < nbThread; i++){
      pthread_create(&(_env->getThread()[i]), NULL, &computationInt, (void *)(_env));
    }
    for(int i=0; i< nbThread; i++){
      // Wait for all the INT threads to finish
      pthread_join(_env->getThread()[i], NULL);
    }
    cout << "Result for Integer Computation : " << _env->getResult() << "IOPS" << endl ;
    cerr << _env->getResult() << ";" << endl ;  
    _env->resetResult();
    // Launch threads for FLOAT computations
    for (int i =0; i < nbThread; i++){
      pthread_create(&(_env->getThread()[i]), NULL, &computationFloat, (void *)(_env));
    }
    for(int i=0; i< nbThread; i++){
      // Wait for all the FLOAT threads to finish
      pthread_join(_env->getThread()[i], NULL);
    }

    cout << "Result for Floating computation : " << _env->getResult() << "IOPS" << endl ; 
    cerr << ";" << _env->getResult() << ";" << endl ; 
    break;
  }

 
}

Environment * Computation::getEnvironment(){
  return _env; 
}

void Computation::setEnvironment(Environment * env){
  _env = env; 
}
